/**
 * @fileOverview Graph插件之VML
 * @author <a href="mailto:lexloo@126.com">lexloo</a>
 */
Gear.kernel.DefPlugIn('plugIns/graph/vml.js', function() {
	var GG = Gear.Graph;
	var doc = document;
	var concat = 'concat';
	var appendChild = "appendChild",
	var rp = 'replace';
	var E = '';
	var S = ' ';
	var apply = 'apply';
	var formatrg = /\{(\d+)\}/g;
	var length = 'length';
	var createNode;
	var coordsize = 1e3 + S + 1e3;
	var coordorigin = '0 0';
	
    GG[toString] = function () {
        return  "浏览器不支持 SVG，使用VML。";
    };
    
    GG.path = function (pathString) {
        pathString && !$Lang.is(pathString, 'string') && !$Lang.is(pathString[0], 'array') && (pathString += E);
        
        return thePath(GG.format[apply](this, arguments), this);
    };
    
    GG.rect = function (x, y, w, h, r) {
        return theRect(this, x || 0, y || 0, w || 0, h || 0, r || 0);
    };
    
    GG.format = function (token, params) {
        var args = $Lang.is(params, 'array') ? [0][concat](params) : arguments;
        token && $Lang.is(token, 'string') && args[length] - 1 && (token = token[rp](formatrg, function (str, i) {
            return args[++i] == null ? E : args[i];
        }));
        return token || E;
    };
    
    var thePath = function (pathString, vml) {
        var g = createNode("group");
        g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
        g.coordsize = coordsize;
        g.coordorigin = coordorigin;
        
        var el = createNode("shape");
        var ol = el.style;
        ol.width = vml.width + "px";
        ol.height = vml.height + "px";
        el.coordsize = coordsize;
        el.coordorigin = coordorigin;
        g[appendChild](el);
        
        var p = new Element(el, g, vml);
        attr = {fill: "none", stroke: "#000"};
        pathString && (attr.path = pathString);
        p.type = "path";
        p.path = [];
        p.Path = E;
        setFillAndStroke(p, attr);
        vml.canvas[appendChild](g);
        
        return p;
    };
    
    /*处理填充和笔画*/
    var setFillAndStroke = function (o, params) {
        o.attrs = o.attrs || {};
        var node = o.node,
            a = o.attrs,
            s = node.style,
            xy,
            newpath = (params.x != a.x || params.y != a.y || params.width != a.width || params.height != a.height || params.r != a.r) && o.type == "rect",
            res = o;

        for (var par in params) if (params[has](par)) {
            a[par] = params[par];
        }
        if (newpath) {
            a.path = rectPath(a.x, a.y, a.width, a.height, a.r);
            o.X = a.x;
            o.Y = a.y;
            o.W = a.width;
            o.H = a.height;
        }
//        params.href && (node.href = params.href);
//        params.title && (node.title = params.title);
//        params.target && (node.target = params.target);
//        params.cursor && (s.cursor = params.cursor);
//        "blur" in params && o.blur(params.blur);
//        if (params.path && o.type == "path" || newpath) {
//            node.path = path2vml(a.path);
//        }
//        if (params.rotation != null) {
//            o.rotate(params.rotation, true);
//        }
//        if (params.translation) {
//            xy = Str(params.translation)[split](separator);
//            translate.call(o, xy[0], xy[1]);
//            if (o._.rt.cx != null) {
//                o._.rt.cx +=+ xy[0];
//                o._.rt.cy +=+ xy[1];
//                o.setBox(o.attrs, xy[0], xy[1]);
//            }
//        }
//        if (params.scale) {
//            xy = Str(params.scale)[split](separator);
//            o.scale(+xy[0] || 1, +xy[1] || +xy[0] || 1, +xy[2] || null, +xy[3] || null);
//        }
//        if ("clip-rect" in params) {
//            var rect = Str(params["clip-rect"])[split](separator);
//            if (rect[length] == 4) {
//                rect[2] = +rect[2] + (+rect[0]);
//                rect[3] = +rect[3] + (+rect[1]);
//                var div = node.clipRect || doc.createElement("div"),
//                    dstyle = div.style,
//                    group = node.parentNode;
//                dstyle.clip = R.format("rect({1}px {2}px {3}px {0}px)", rect);
//                if (!node.clipRect) {
//                    dstyle.position = "absolute";
//                    dstyle.top = 0;
//                    dstyle.left = 0;
//                    dstyle.width = o.paper.width + "px";
//                    dstyle.height = o.paper.height + "px";
//                    group.parentNode.insertBefore(div, group);
//                    div[appendChild](group);
//                    node.clipRect = div;
//                }
//            }
//            if (!params["clip-rect"]) {
//                node.clipRect && (node.clipRect.style.clip = E);
//            }
//        }
//        if (o.type == "image" && params.src) {
//            node.src = params.src;
//        }
//        if (o.type == "image" && params.opacity) {
//            node.filterOpacity = ms + ".Alpha(opacity=" + (params.opacity * 100) + ")";
//            s.filter = (node.filterMatrix || E) + (node.filterOpacity || E);
//        }
//        params.font && (s.font = params.font);
//        params["font-family"] && (s.fontFamily = '"' + params["font-family"][split](",")[0][rp](/^['"]+|['"]+$/g, E) + '"');
//        params["font-size"] && (s.fontSize = params["font-size"]);
//        params["font-weight"] && (s.fontWeight = params["font-weight"]);
//        params["font-style"] && (s.fontStyle = params["font-style"]);
//        if (params.opacity != null || 
//            params["stroke-width"] != null ||
//            params.fill != null ||
//            params.stroke != null ||
//            params["stroke-width"] != null ||
//            params["stroke-opacity"] != null ||
//            params["fill-opacity"] != null ||
//            params["stroke-dasharray"] != null ||
//            params["stroke-miterlimit"] != null ||
//            params["stroke-linejoin"] != null ||
//            params["stroke-linecap"] != null) {
//            node = o.shape || node;
//            var fill = (node.getElementsByTagName(fillString) && node.getElementsByTagName(fillString)[0]),
//                newfill = false;
//            !fill && (newfill = fill = createNode(fillString));
//            if ("fill-opacity" in params || "opacity" in params) {
//                var opacity = ((+a["fill-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+R.getRGB(params.fill).o + 1 || 2) - 1);
//                opacity = mmin(mmax(opacity, 0), 1);
//                fill.opacity = opacity;
//            }
//            params.fill && (fill.on = true);
//            if (fill.on == null || params.fill == "none") {
//                fill.on = false;
//            }
//            if (fill.on && params.fill) {
//                var isURL = params.fill.match(ISURL);
//                if (isURL) {
//                    fill.src = isURL[1];
//                    fill.type = "tile";
//                } else {
//                    fill.color = R.getRGB(params.fill).hex;
//                    fill.src = E;
//                    fill.type = "solid";
//                    if (R.getRGB(params.fill).error && (res.type in {circle: 1, ellipse: 1} || Str(params.fill).charAt() != "r") && addGradientFill(res, params.fill)) {
//                        a.fill = "none";
//                        a.gradient = params.fill;
//                    }
//                }
//            }
//            newfill && node[appendChild](fill);
//            var stroke = (node.getElementsByTagName("stroke") && node.getElementsByTagName("stroke")[0]),
//            newstroke = false;
//            !stroke && (newstroke = stroke = createNode("stroke"));
//            if ((params.stroke && params.stroke != "none") ||
//                params["stroke-width"] ||
//                params["stroke-opacity"] != null ||
//                params["stroke-dasharray"] ||
//                params["stroke-miterlimit"] ||
//                params["stroke-linejoin"] ||
//                params["stroke-linecap"]) {
//                stroke.on = true;
//            }
//            (params.stroke == "none" || stroke.on == null || params.stroke == 0 || params["stroke-width"] == 0) && (stroke.on = false);
//            var strokeColor = R.getRGB(params.stroke);
//            stroke.on && params.stroke && (stroke.color = strokeColor.hex);
//            opacity = ((+a["stroke-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+strokeColor.o + 1 || 2) - 1);
//            var width = (toFloat(params["stroke-width"]) || 1) * .75;
//            opacity = mmin(mmax(opacity, 0), 1);
//            params["stroke-width"] == null && (width = a["stroke-width"]);
//            params["stroke-width"] && (stroke.weight = width);
//            width && width < 1 && (opacity *= width) && (stroke.weight = 1);
//            stroke.opacity = opacity;
//            
//            params["stroke-linejoin"] && (stroke.joinstyle = params["stroke-linejoin"] || "miter");
//            stroke.miterlimit = params["stroke-miterlimit"] || 8;
//            params["stroke-linecap"] && (stroke.endcap = params["stroke-linecap"] == "butt" ? "flat" : params["stroke-linecap"] == "square" ? "square" : "round");
//            if (params["stroke-dasharray"]) {
//                var dasharray = {
//                    "-": "shortdash",
//                    ".": "shortdot",
//                    "-.": "shortdashdot",
//                    "-..": "shortdashdotdot",
//                    ". ": "dot",
//                    "- ": "dash",
//                    "--": "longdash",
//                    "- .": "dashdot",
//                    "--.": "longdashdot",
//                    "--..": "longdashdotdot"
//                };
//                stroke.dashstyle = dasharray[has](params["stroke-dasharray"]) ? dasharray[params["stroke-dasharray"]] : E;
//            }
//            newstroke && node[appendChild](stroke);
//        }
//        if (res.type == "text") {
//            s = res.paper.span.style;
//            a.font && (s.font = a.font);
//            a["font-family"] && (s.fontFamily = a["font-family"]);
//            a["font-size"] && (s.fontSize = a["font-size"]);
//            a["font-weight"] && (s.fontWeight = a["font-weight"]);
//            a["font-style"] && (s.fontStyle = a["font-style"]);
//            res.node.string && (res.paper.span.innerHTML = Str(res.node.string)[rp](/</g, "&#60;")[rp](/&/g, "&#38;")[rp](/\n/g, "<br>"));
//            res.W = a.w = res.paper.span.offsetWidth;
//            res.H = a.h = res.paper.span.offsetHeight;
//            res.X = a.x;
//            res.Y = a.y + round(res.H / 2);
//
//            // text-anchor emulationm
//            switch (a["text-anchor"]) {
//                case "start":
//                    res.node.style["v-text-align"] = "left";
//                    res.bbx = round(res.W / 2);
//                break;
//                case "end":
//                    res.node.style["v-text-align"] = "right";
//                    res.bbx = -round(res.W / 2);
//                break;
//                default:
//                    res.node.style["v-text-align"] = "center";
//                break;
//            }
//        }
    };
    
    var theRect = function (vml, x, y, w, h, r) {
        var path = rectPath(x, y, w, h, r),
            res = vml.path(path),
            a = res.attrs;
        res.X = a.x = x;
        res.Y = a.y = y;
        res.W = a.width = w;
        res.H = a.height = h;
        a.r = r;
        a.path = path;
        res.type = "rect";
        return res;
    };
    
    var rectPath =function (x, y, w, h, r) {
        if (r) {
            return GG.format("M{0},{1}l{2},0a{3},{3},0,0,1,{3},{3}l0,{5}a{3},{3},0,0,1,{4},{3}l{6},0a{3},{3},0,0,1,{4},{4}l0,{7}a{3},{3},0,0,1,{3},{4}z", x + r, y, w - r * 2, r, -r, h - r * 2, r * 2 - w, r * 2 - h);
        } else {
            return GG.format("M{0},{1}l{2},0,0,{3},{4},0z", x, y, w, h, -w);
        }
    }
    
    doc.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
    try {
        !doc.namespaces.rvml && doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
        createNode = function (tagName) {
            return doc.createElement('<rvml:' + tagName + ' class="rvml">');
        };
    } catch (e) {
        createNode = function (tagName) {
            return doc.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
        };
    }
    
	    var separator = /[, ]+/,
	        elements = {circle: 1, rect: 1, path: 1, ellipse: 1, text: 1, image: 1},
	        
	        proto = "prototype",
	        has = "hasOwnProperty",
	        doc = document,
	        win = window,
	        oldRaphael = {
	            was: Object[proto][has].call(win, "Raphael"),
	            is: win.Raphael
	        },
	        Paper = function () {
	            this.customAttributes = {};
	        },
	        paperproto,
	        
	        concat = "concat",
	        supportsTouch = "createTouch" in doc,
	        E = "",
	        S = " ",
	        Str = String,
	        split = "split",
	        events = "click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend orientationchange touchcancel gesturestart gesturechange gestureend"[split](S),
	        touchMap = {
	            mousedown: "touchstart",
	            mousemove: "touchmove",
	            mouseup: "touchend"
	        },
	        join = "join",
	        length = "length",
	        lowerCase = Str[proto].toLowerCase,
	        math = Math,
	        mmax = math.max,
	        mmin = math.min,
	        abs = math.abs,
	        pow = math.pow,
	        PI = math.PI,
	        nu = "number",
	        string = "string",
	        array = "array",
	        toString = "toString",
	        fillString = "fill",
	        objectToString = Object[proto][toString],
	        paper = {},
	        push = "push",
	        ISURL = /^url\(['"]?([^\)]+?)['"]?\)$/i,
	        colourRegExp = /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i,
	        isnan = {"NaN": 1, "Infinity": 1, "-Infinity": 1},
	        bezierrg = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,
	        round = math.round,
	        setAttribute = "setAttribute",
	        toFloat = parseFloat,
	        toInt = parseInt,
	        ms = " progid:DXImageTransform.Microsoft",
	        upperCase = Str[proto].toUpperCase,
	        availableAttrs = {blur: 0, "clip-rect": "0 0 1e9 1e9", cursor: "default", cx: 0, cy: 0, fill: "#fff", "fill-opacity": 1, font: '10px "Arial"', "font-family": '"Arial"', "font-size": "10", "font-style": "normal", "font-weight": 400, gradient: 0, height: 0, href: "http://raphaeljs.com/", opacity: 1, path: "M0,0", r: 0, rotation: 0, rx: 0, ry: 0, scale: "1 1", src: "", stroke: "#000", "stroke-dasharray": "", "stroke-linecap": "butt", "stroke-linejoin": "butt", "stroke-miterlimit": 0, "stroke-opacity": 1, "stroke-width": 1, target: "_blank", "text-anchor": "middle", title: "Raphael", translation: "0 0", width: 0, x: 0, y: 0},
	        availableAnimAttrs = {along: "along", blur: nu, "clip-rect": "csv", cx: nu, cy: nu, fill: "colour", "fill-opacity": nu, "font-size": nu, height: nu, opacity: nu, path: "path", r: nu, rotation: "csv", rx: nu, ry: nu, scale: "csv", stroke: "colour", "stroke-opacity": nu, "stroke-width": nu, translation: "csv", width: nu, x: nu, y: nu},
	        rp = "replace",
	        animKeyFrames= /^(from|to|\d+%?)$/,
	        commaSpaces = /\s*,\s*/,
	        hsrg = {hs: 1, rg: 1},
	        p2s = /,?([achlmqrstvxz]),?/gi,
	        pathCommand = /([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,
	        pathValues = /(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig,
	        radial_gradient = /^r(?:\(([^,]+?)\s*,\s*([^\)]+?)\))?/,
	        sortByKey = function (a, b) {
	            return a.key - b.key;
	        };

	        var map = {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"},
	            bites = /([clmz]),?([^clmz]*)/gi,
	            blurregexp = / progid:\S+Blur\([^\)]+\)/g,
	            val = /-?[^,\s-]+/g,
	            
	            zoom = 10,
	            pathlike = {path: 1, rect: 1},
	            path2vml = function (path) {
	                var total =  /[ahqstv]/ig,
	                    command = pathToAbsolute;
	                Str(path).match(total) && (command = path2curve);
	                total = /[clmz]/g;
	                if (command == pathToAbsolute && !Str(path).match(total)) {
	                    var res = Str(path)[rp](bites, function (all, command, args) {
	                        var vals = [],
	                            isMove = lowerCase.call(command) == "m",
	                            res = map[command];
	                        args[rp](val, function (value) {
	                            if (isMove && vals[length] == 2) {
	                                res += vals + map[command == "m" ? "l" : "L"];
	                                vals = [];
	                            }
	                            vals[push](round(value * zoom));
	                        });
	                        return res + vals;
	                    });
	                    return res;
	                }
	                var pa = command(path), p, r;
	                res = [];
	                for (var i = 0, ii = pa[length]; i < ii; i++) {
	                    p = pa[i];
	                    r = lowerCase.call(pa[i][0]);
	                    r == "z" && (r = "x");
	                    for (var j = 1, jj = p[length]; j < jj; j++) {
	                        r += round(p[j] * zoom) + (j != jj - 1 ? "," : E);
	                    }
	                    res[push](r);
	                }
	                return res[join](S);
	            };
	        

	        

	        addGradientFill = function (o, gradient) {
	            o.attrs = o.attrs || {};
	            var attrs = o.attrs,
	                fill,
	                type = "linear",
	                fxfy = ".5 .5";
	            o.attrs.gradient = gradient;
	            gradient = Str(gradient)[rp](radial_gradient, function (all, fx, fy) {
	                type = "radial";
	                if (fx && fy) {
	                    fx = toFloat(fx);
	                    fy = toFloat(fy);
	                    pow(fx - .5, 2) + pow(fy - .5, 2) > .25 && (fy = math.sqrt(.25 - pow(fx - .5, 2)) * ((fy > .5) * 2 - 1) + .5);
	                    fxfy = fx + S + fy;
	                }
	                return E;
	            });
	            gradient = gradient[split](/\s*\-\s*/);
	            if (type == "linear") {
	                var angle = gradient.shift();
	                angle = -toFloat(angle);
	                if (isNaN(angle)) {
	                    return null;
	                }
	            }
	            var dots = parseDots(gradient);
	            if (!dots) {
	                return null;
	            }
	            o = o.shape || o.node;
	            fill = o.getElementsByTagName(fillString)[0] || createNode(fillString);
	            !fill.parentNode && o.appendChild(fill);
	            if (dots[length]) {
	                fill.on = true;
	                fill.method = "none";
	                fill.color = dots[0].color;
	                fill.color2 = dots[dots[length] - 1].color;
	                var clrs = [];
	                for (var i = 0, ii = dots[length]; i < ii; i++) {
	                    dots[i].offset && clrs[push](dots[i].offset + S + dots[i].color);
	                }
	                fill.colors && (fill.colors.value = clrs[length] ? clrs[join]() : "0% " + fill.color);
	                if (type == "radial") {
	                    fill.type = "gradientradial";
	                    fill.focus = "100%";
	                    fill.focussize = fxfy;
	                    fill.focusposition = fxfy;
	                } else {
	                    fill.type = "gradient";
	                    fill.angle = (270 - angle) % 360;
	                }
	            }
	            return 1;
	        };
	        Element = function (node, group, vml) {
	            var Rotation = 0,
	                RotX = 0,
	                RotY = 0,
	                Scale = 1;
	            this[0] = node;
	            this.id = R._oid++;
	            this.node = node;
	            node.raphael = this;
	            this.X = 0;
	            this.Y = 0;
	            this.attrs = {};
	            this.Group = group;
	            this.paper = vml;
	            this._ = {
	                tx: 0,
	                ty: 0,
	                rt: {deg:0},
	                sx: 1,
	                sy: 1
	            };
	            !vml.bottom && (vml.bottom = this);
	            this.prev = vml.top;
	            vml.top && (vml.top.next = this);
	            vml.top = this;
	            this.next = null;
	        };
	        elproto = Element[proto];
	        elproto.rotate = function (deg, cx, cy) {
	            if (this.removed) {
	                return this;
	            }
	            if (deg == null) {
	                if (this._.rt.cx) {
	                    return [this._.rt.deg, this._.rt.cx, this._.rt.cy][join](S);
	                }
	                return this._.rt.deg;
	            }
	            deg = Str(deg)[split](separator);
	            if (deg[length] - 1) {
	                cx = toFloat(deg[1]);
	                cy = toFloat(deg[2]);
	            }
	            deg = toFloat(deg[0]);
	            if (cx != null) {
	                this._.rt.deg = deg;
	            } else {
	                this._.rt.deg += deg;
	            }
	            cy == null && (cx = null);
	            this._.rt.cx = cx;
	            this._.rt.cy = cy;
	            this.setBox(this.attrs, cx, cy);
	            this.Group.style.rotation = this._.rt.deg;
	            // gradient fix for rotation. TODO
	            // var fill = (this.shape || this.node).getElementsByTagName(fillString);
	            // fill = fill[0] || {};
	            // var b = ((360 - this._.rt.deg) - 270) % 360;
	            // !R.is(fill.angle, "undefined") && (fill.angle = b);
	            return this;
	        };
	        elproto.setBox = function (params, cx, cy) {
	            if (this.removed) {
	                return this;
	            }
	            var gs = this.Group.style,
	                os = (this.shape && this.shape.style) || this.node.style;
	            params = params || {};
	            for (var i in params) if (params[has](i)) {
	                this.attrs[i] = params[i];
	            }
	            cx = cx || this._.rt.cx;
	            cy = cy || this._.rt.cy;
	            var attr = this.attrs,
	                x,
	                y,
	                w,
	                h;
	            switch (this.type) {
	                case "circle":
	                    x = attr.cx - attr.r;
	                    y = attr.cy - attr.r;
	                    w = h = attr.r * 2;
	                    break;
	                case "ellipse":
	                    x = attr.cx - attr.rx;
	                    y = attr.cy - attr.ry;
	                    w = attr.rx * 2;
	                    h = attr.ry * 2;
	                    break;
	                case "image":
	                    x = +attr.x;
	                    y = +attr.y;
	                    w = attr.width || 0;
	                    h = attr.height || 0;
	                    break;
	                case "text":
	                    this.textpath.v = ["m", round(attr.x), ", ", round(attr.y - 2), "l", round(attr.x) + 1, ", ", round(attr.y - 2)][join](E);
	                    x = attr.x - round(this.W / 2);
	                    y = attr.y - this.H / 2;
	                    w = this.W;
	                    h = this.H;
	                    break;
	                case "rect":
	                case "path":
	                    if (!this.attrs.path) {
	                        x = 0;
	                        y = 0;
	                        w = this.paper.width;
	                        h = this.paper.height;
	                    } else {
	                        var dim = pathDimensions(this.attrs.path);
	                        x = dim.x;
	                        y = dim.y;
	                        w = dim.width;
	                        h = dim.height;
	                    }
	                    break;
	                default:
	                    x = 0;
	                    y = 0;
	                    w = this.paper.width;
	                    h = this.paper.height;
	                    break;
	            }
	            cx = (cx == null) ? x + w / 2 : cx;
	            cy = (cy == null) ? y + h / 2 : cy;
	            var left = cx - this.paper.width / 2,
	                top = cy - this.paper.height / 2, t;
	            gs.left != (t = left + "px") && (gs.left = t);
	            gs.top != (t = top + "px") && (gs.top = t);
	            this.X = pathlike[has](this.type) ? -left : x;
	            this.Y = pathlike[has](this.type) ? -top : y;
	            this.W = w;
	            this.H = h;
	            if (pathlike[has](this.type)) {
	                os.left != (t = -left * zoom + "px") && (os.left = t);
	                os.top != (t = -top * zoom + "px") && (os.top = t);
	            } else if (this.type == "text") {
	                os.left != (t = -left + "px") && (os.left = t);
	                os.top != (t = -top + "px") && (os.top = t);
	            } else {
	                gs.width != (t = this.paper.width + "px") && (gs.width = t);
	                gs.height != (t = this.paper.height + "px") && (gs.height = t);
	                os.left != (t = x - left + "px") && (os.left = t);
	                os.top != (t = y - top + "px") && (os.top = t);
	                os.width != (t = w + "px") && (os.width = t);
	                os.height != (t = h + "px") && (os.height = t);
	            }
	        };
	        elproto.hide = function () {
	            !this.removed && (this.Group.style.display = "none");
	            return this;
	        };
	        elproto.show = function () {
	            !this.removed && (this.Group.style.display = "block");
	            return this;
	        };
	        elproto.getBBox = function () {
	            if (this.removed) {
	                return this;
	            }
	            if (pathlike[has](this.type)) {
	                return pathDimensions(this.attrs.path);
	            }
	            return {
	                x: this.X + (this.bbx || 0),
	                y: this.Y,
	                width: this.W,
	                height: this.H
	            };
	        };
	        elproto.remove = function () {
	            if (this.removed) {
	                return;
	            }
	            tear(this, this.paper);
	            this.node.parentNode.removeChild(this.node);
	            this.Group.parentNode.removeChild(this.Group);
	            this.shape && this.shape.parentNode.removeChild(this.shape);
	            for (var i in this) {
	                delete this[i];
	            }
	            this.removed = true;
	        };
	        elproto.attr = function (name, value) {
	            if (this.removed) {
	                return this;
	            }
	            if (name == null) {
	                var res = {};
	                for (var i in this.attrs) if (this.attrs[has](i)) {
	                    res[i] = this.attrs[i];
	                }
	                this._.rt.deg && (res.rotation = this.rotate());
	                (this._.sx != 1 || this._.sy != 1) && (res.scale = this.scale());
	                res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
	                return res;
	            }
	            if (value == null && R.is(name, "string")) {
	                if (name == "translation") {
	                    return translate.call(this);
	                }
	                if (name == "rotation") {
	                    return this.rotate();
	                }
	                if (name == "scale") {
	                    return this.scale();
	                }
	                if (name == fillString && this.attrs.fill == "none" && this.attrs.gradient) {
	                    return this.attrs.gradient;
	                }
	                return this.attrs[name];
	            }
	            if (this.attrs && value == null && R.is(name, array)) {
	                var ii, values = {};
	                for (i = 0, ii = name[length]; i < ii; i++) {
	                    values[name[i]] = this.attr(name[i]);
	                }
	                return values;
	            }
	            var params;
	            if (value != null) {
	                params = {};
	                params[name] = value;
	            }
	            value == null && R.is(name, "object") && (params = name);
	            if (params) {
	                for (var key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) {
	                    var par = this.paper.customAttributes[key].apply(this, [][concat](params[key]));
	                    this.attrs[key] = params[key];
	                    for (var subkey in par) if (par[has](subkey)) {
	                        params[subkey] = par[subkey];
	                    }
	                }
	                if (params.text && this.type == "text") {
	                    this.node.string = params.text;
	                }
	                setFillAndStroke(this, params);
	                if (params.gradient && (({circle: 1, ellipse: 1})[has](this.type) || Str(params.gradient).charAt() != "r")) {
	                    addGradientFill(this, params.gradient);
	                }
	                (!pathlike[has](this.type) || this._.rt.deg) && this.setBox(this.attrs);
	            }
	            return this;
	        };
	        elproto.toFront = function () {
	            !this.removed && this.Group.parentNode[appendChild](this.Group);
	            this.paper.top != this && tofront(this, this.paper);
	            return this;
	        };
	        elproto.toBack = function () {
	            if (this.removed) {
	                return this;
	            }
	            if (this.Group.parentNode.firstChild != this.Group) {
	                this.Group.parentNode.insertBefore(this.Group, this.Group.parentNode.firstChild);
	                toback(this, this.paper);
	            }
	            return this;
	        };
	        elproto.insertAfter = function (element) {
	            if (this.removed) {
	                return this;
	            }
	            if (element.constructor == Set) {
	                element = element[element.length - 1];
	            }
	            if (element.Group.nextSibling) {
	                element.Group.parentNode.insertBefore(this.Group, element.Group.nextSibling);
	            } else {
	                element.Group.parentNode[appendChild](this.Group);
	            }
	            insertafter(this, element, this.paper);
	            return this;
	        };
	        elproto.insertBefore = function (element) {
	            if (this.removed) {
	                return this;
	            }
	            if (element.constructor == Set) {
	                element = element[0];
	            }
	            element.Group.parentNode.insertBefore(this.Group, element.Group);
	            insertbefore(this, element, this.paper);
	            return this;
	        };
	        elproto.blur = function (size) {
	            var s = this.node.runtimeStyle,
	                f = s.filter;
	            f = f.replace(blurregexp, E);
	            if (+size !== 0) {
	                this.attrs.blur = size;
	                s.filter = f + S + ms + ".Blur(pixelradius=" + (+size || 1.5) + ")";
	                s.margin = R.format("-{0}px 0 0 -{0}px", round(+size || 1.5));
	            } else {
	                s.filter = f;
	                s.margin = 0;
	                delete this.attrs.blur;
	            }
	        };
	 
	        theCircle = function (vml, x, y, r) {
	            var g = createNode("group"),
	                o = createNode("oval"),
	                ol = o.style;
	            g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
	            g.coordsize = coordsize;
	            g.coordorigin = vml.coordorigin;
	            g[appendChild](o);
	            var res = new Element(o, g, vml);
	            res.type = "circle";
	            setFillAndStroke(res, {stroke: "#000", fill: "none"});
	            res.attrs.cx = x;
	            res.attrs.cy = y;
	            res.attrs.r = r;
	            res.setBox({x: x - r, y: y - r, width: r * 2, height: r * 2});
	            vml.canvas[appendChild](g);
	            return res;
	        };
	      

	        theEllipse = function (vml, x, y, rx, ry) {
	            var g = createNode("group"),
	                o = createNode("oval"),
	                ol = o.style;
	            g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
	            g.coordsize = coordsize;
	            g.coordorigin = vml.coordorigin;
	            g[appendChild](o);
	            var res = new Element(o, g, vml);
	            res.type = "ellipse";
	            setFillAndStroke(res, {stroke: "#000"});
	            res.attrs.cx = x;
	            res.attrs.cy = y;
	            res.attrs.rx = rx;
	            res.attrs.ry = ry;
	            res.setBox({x: x - rx, y: y - ry, width: rx * 2, height: ry * 2});
	            vml.canvas[appendChild](g);
	            return res;
	        };
	        theImage = function (vml, src, x, y, w, h) {
	            var g = createNode("group"),
	                o = createNode("image");
	            g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
	            g.coordsize = coordsize;
	            g.coordorigin = vml.coordorigin;
	            o.src = src;
	            g[appendChild](o);
	            var res = new Element(o, g, vml);
	            res.type = "image";
	            res.attrs.src = src;
	            res.attrs.x = x;
	            res.attrs.y = y;
	            res.attrs.w = w;
	            res.attrs.h = h;
	            res.setBox({x: x, y: y, width: w, height: h});
	            vml.canvas[appendChild](g);
	            return res;
	        };
	        theText = function (vml, x, y, text) {
	            var g = createNode("group"),
	                el = createNode("shape"),
	                ol = el.style,
	                path = createNode("path"),
	                ps = path.style,
	                o = createNode("textpath");
	            g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
	            g.coordsize = coordsize;
	            g.coordorigin = vml.coordorigin;
	            path.v = R.format("m{0},{1}l{2},{1}", round(x * 10), round(y * 10), round(x * 10) + 1);
	            path.textpathok = true;
	            ol.width = vml.width;
	            ol.height = vml.height;
	            o.string = Str(text);
	            o.on = true;
	            el[appendChild](o);
	            el[appendChild](path);
	            g[appendChild](el);
	            var res = new Element(o, g, vml);
	            res.shape = el;
	            res.textpath = path;
	            res.type = "text";
	            res.attrs.text = text;
	            res.attrs.x = x;
	            res.attrs.y = y;
	            res.attrs.w = 1;
	            res.attrs.h = 1;
	            setFillAndStroke(res, {font: availableAttrs.font, stroke: "none", fill: "#000"});
	            res.setBox();
	            vml.canvas[appendChild](g);
	            return res;
	        };
	        setSize = function (width, height) {
	            var cs = this.canvas.style;
	            width == +width && (width += "px");
	            height == +height && (height += "px");
	            cs.width = width;
	            cs.height = height;
	            cs.clip = "rect(0 " + width + " " + height + " 0)";
	            return this;
	        };
	        
	        create = function () {
	            var con = getContainer[apply](0, arguments),
	                container = con.container,
	                height = con.height,
	                s,
	                width = con.width,
	                x = con.x,
	                y = con.y;
	            if (!container) {
	                throw new Error("VML container not found.");
	            }
	            var res = new Paper,
	                c = res.canvas = doc.createElement("div"),
	                cs = c.style;
	            x = x || 0;
	            y = y || 0;
	            width = width || 512;
	            height = height || 342;
	            width == +width && (width += "px");
	            height == +height && (height += "px");
	            res.width = 1e3;
	            res.height = 1e3;
	            res.coordsize = zoom * 1e3 + S + zoom * 1e3;
	            res.coordorigin = "0 0";
	            res.span = doc.createElement("span");
	            res.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";
	            c[appendChild](res.span);
	            cs.cssText = R.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden", width, height);
	            if (container == 1) {
	                doc.body[appendChild](c);
	                cs.left = x + "px";
	                cs.top = y + "px";
	                cs.position = "absolute";
	            } else {
	                if (container.firstChild) {
	                    container.insertBefore(c, container.firstChild);
	                } else {
	                    container[appendChild](c);
	                }
	            }
	            plugins.call(res, res, R.fn);
	            return res;
	        };
	        paperproto.clear = function () {
	            this.canvas.innerHTML = E;
	            this.span = doc.createElement("span");
	            this.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";
	            this.canvas[appendChild](this.span);
	            this.bottom = this.top = null;
	        };
	        paperproto.remove = function () {
	            this.canvas.parentNode.removeChild(this.canvas);
	            for (var i in this) {
	                this[i] = removed(i);
	            }
	            return true;
	        };
});